//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <string.h>
#include <mem/util.h>
#include <mem/heap.h>
#include <core/portio.h>
#include <process/common.h>
#include <core/intrcommon.h>

// Defines

// TODO: remove when rewritten
#define NEEDS_REWRITE_SO_REMOVED

// the maximum size for a mailbox's name
#define MAXLENGTH_MAILBOX_NAME	128

// the maximum number of mailboxes
#define MAXNUMBER_MAILBOXES		1024

// Types

// a message in a mailbox
struct msg {
	// whether this message is valid or not
	int valid;

	// the data of the message
	char* msgdata;

	// length of data
	uint32_t msglen;

	// the next and previous messages
	struct msg* prev;
	struct msg* next;
};

// Variables

// the mailbox list (the "post office", if you will)
struct mbox {
	// available for use?
	int available;

	// the name of this mailbox
	char name[MAXLENGTH_MAILBOX_NAME];

	// the messages in this mailbox (FIFO queue)
	struct msg* messages;

	// the head of the message list
	struct msg* head;

	// the tail of the message list
	struct msg* tail;
} *postoffice;

// Functions

// sets up the mailbox system
void InitMailboxes()
{
#ifdef NEEDS_REWRITE_SO_REMOVED
	return;
#endif

	// create the post office
	postoffice = (struct mbox*) malloc( MAXNUMBER_MAILBOXES * sizeof( struct mbox ) );

	// set all mailboxes to available and void their message lists
	int i;
	for( i = 0; i < MAXNUMBER_MAILBOXES; i++ )
	{
		postoffice[i].available = 1;
		postoffice[i].messages = 0;
		memset( postoffice[i].name, 0, MAXLENGTH_MAILBOX_NAME );
	}
}

// creates a new mailbox
int CreateMailbox( char* name )
{
#ifdef NEEDS_REWRITE_SO_REMOVED
	return -1;
#endif

	// find a free mailbox entry
	int i;
	for( i = 0; i < MAXNUMBER_MAILBOXES; i++ )
		if( postoffice[i].available )
			break;
	if( i == MAXNUMBER_MAILBOXES )
		return -1; // we failed to find a valid mailbox

	// we can't use this entry anymore!
	postoffice[i].available = 0;

	// now, copy in the name, keeping in mind the limits
	name[MAXLENGTH_MAILBOX_NAME] = 0;
	strcpy( postoffice[i].name, name );

	// setup the first message (which will always be at the end and is always invalid)
	if( postoffice[i].messages ) // check for a previous invalid mailbox delete
		free( postoffice[i].messages );
	postoffice[i].messages = 0; // (struct msg*) malloc( sizeof( struct msg ) );
	//postoffice[i].messages->next = postoffice[i].messages->prev = 0;
	//postoffice[i].messages->valid = 0;
	postoffice[i].head = postoffice[i].tail = 0;

	// success!
	return 0;
}

// deletes a mailbox
int RemoveMailbox( char* mbox )
{
#ifdef NEEDS_REWRITE_SO_REMOVED
	return -1;
#endif

	// find it in the list
	int i;
	for( i = 0; i < MAXNUMBER_MAILBOXES; i++ )
		if( !postoffice[i].available && strcmp( mbox, postoffice[i].name ) == 0 )
			break;
	if( i == MAXNUMBER_MAILBOXES )
		return -1;

	// clean up any messages on the list
	struct msg* head = postoffice[i].head;
	struct msg* prev = head;
	while( head )
	{
		prev = head;
		head = head->next;
		if( prev->valid )
			free( prev->msgdata );
		free( prev );
	}
	postoffice[i].head = postoffice[i].tail = 0;

	// make it available for use
	postoffice[i].available = 1;

	// zero out the text string as well
	memset( postoffice[i].name, 0, MAXLENGTH_MAILBOX_NAME );

	// success
	return 0;
}

// posts a message to a mailbox
int PostMessage( char* mbox, char* msgdata, uint32_t msglen )
{
#ifdef NEEDS_REWRITE_SO_REMOVED
	return -1;
#endif

	// limit the message size two four pages (which is a LOT of space - 16kb)
	if( msglen > 0x4000 )
		return -1;

	// find the mailbox
	int i;
	for( i = 0; i < MAXNUMBER_MAILBOXES; i++ )
		if( !postoffice[i].available && strcmp( mbox, postoffice[i].name ) == 0 )
			break;
	if( i == MAXNUMBER_MAILBOXES )
		return -1;

	// create space for this message
	struct msg* m = (struct msg*) malloc( sizeof( struct msg ) );

	// setup common information
	m->valid = 1;
	m->msglen = msglen;
	m->msgdata = (char*) malloc( msglen );
	memcpy( m->msgdata, msgdata, msglen );

	// check to see if the message list in the post office is valid or not
	if( !postoffice[i].messages || postoffice[i].messages->valid == 0 )
	{
		// no previous, no next
		m->prev = m->next = 0;

		// free the current message if there's no valid message there
		if( postoffice[i].messages && postoffice[i].messages->valid == 0 )
			free( postoffice[i].messages );

		// not valid, so we set *as* the message list pointer
		postoffice[i].messages = m;

		// setup the head and tail
		postoffice[i].head = postoffice[i].tail = m;
	}
	else
	{
		// link it into the list (it's a FIFO queue, so it needs to go at the end)
		struct msg* tail = postoffice[i].tail;
		m->next = 0;
		m->prev  = tail;
		tail->next = m->next;

		// we're now the new tail of the list
		postoffice[i].tail = m;
	}

	// all done
	return 0;
}

// gets a message from a mailbox, maxlen = 0 means "peek" at the message (ie, get its size)
int GetMessage( char* mbox, char* msgdata, uint32_t maxlen )
{
#ifdef NEEDS_REWRITE_SO_REMOVED
	return -1;
#endif

	// the return value
	int ret = 0;

	// check that the caller is asking for a valid length
	if( maxlen > 0x4000 )
		return -1;

	// find the mailbox
	int i;
	for( i = 0; i < MAXNUMBER_MAILBOXES; i++ )
		if( !postoffice[i].available && strcmp( mbox, postoffice[i].name ) == 0 )
			break;
	if( i == MAXNUMBER_MAILBOXES )
		return -1;

	// get the head and tail of the list and start searching
	struct msg* head = postoffice[i].head;
	//struct msg* tail = postoffice[i].tail;

	// if the head message is not valid, then none of them will be
	if( head->valid == 0 )
	{
		// no valid messages, so we fail
		return -1;
	}
	else
	{
		// grab information about the message
		if( maxlen == 0 )
		{
			// just return the size, DO NOT unlink!
			return head->msglen;
		}

		// take that message and unlink it - the next entry is now the head
		if( head->next )
			head->next->prev = 0;
		postoffice[i].head = head->next;

		// copy the message data across
		memcpy( msgdata, head->msgdata, (maxlen > head->msglen ? head->msglen : maxlen) );

		// we've read in all those bytes - i *know* that this is unsigned
		// to signed assignment, but the maximum length is well within the
		// the range of both, and using an unsigned value for the length
		// means things like "-1" overflow and still give an invalid length
		ret = maxlen;

		// free the message
		free( head );
	}

	// return the length read
	return ret;
}
